home *** CD-ROM | disk | FTP | other *** search
/ Nebula 1 / Nebula One.iso / Communications / pcomm / Source / vcs.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-06-12  |  10.8 KB  |  507 lines

  1. /*
  2.  * Routines for VCS detection.
  3.  */
  4.  
  5. #include <stdio.h>
  6. #include "config.h"
  7. #include "vcs.h"
  8.  
  9. #ifndef OLDCURSES
  10. #include <curses.h>
  11. #include <term.h>
  12. #else /* OLDCURSES */
  13. char tcbuf[1024];
  14. #endif /* OLDCURSES */
  15.  
  16. static int putc_cnt;
  17. static char putc_buf[VCS_SIZE];
  18.  
  19. /*
  20.  * Test for possible VCS (video command sequence).  A character return
  21.  * code means no match.  An return code greater than 255 means a VCS
  22.  * was found.
  23.  */
  24.  
  25. int
  26. vcs_filter(c)
  27. char c;
  28. {
  29.     extern int vcs_codes[NUM_VCS][VCS_SIZE], vcs_leadin[NUM_VCS];
  30.     extern int num_leadin;
  31.     static int vcs_buf[VCS_SIZE], match_codes();
  32.     static int ptr = 0;
  33.     register int i;
  34.     int maybe, possible;
  35.  
  36.                     /* see if possible */
  37.     possible = 0;
  38.     if (ptr == 0) {
  39.         /*
  40.          * This is kinda crude... I'm checking to see if the
  41.          * lead-in character is greater than the space character.
  42.          * If so, it most probably is NOT a VCS.
  43.          */
  44.         if (c >= ' ')
  45.             return(c & 0xff);
  46.                     /* check the list */
  47.         for (i=0; i<num_leadin; i++) {
  48.             if (c == vcs_leadin[i]) {
  49.                 possible++;
  50.                 break;
  51.             }
  52.         }
  53.         if (!possible)
  54.             return(c & 0xff);
  55.     }
  56.  
  57.                     /* build the string */
  58.     vcs_buf[ptr++] = c;
  59.     vcs_buf[ptr] = -1;
  60.                     /* test for match */
  61.     maybe = 0;
  62.     for (i=0; i<NUM_VCS; i++) {
  63.         switch (match_codes(vcs_buf, vcs_codes[i], i)) {
  64.             case YES:
  65.                 ptr = 0;
  66.                 return(i+256);
  67.             case NO:
  68.                 break;
  69.             case MAYBE:
  70.                 maybe++;
  71.                 break;
  72.         }
  73.     }
  74.                     /* abandon what you've got */
  75.     if (maybe && ptr == VCS_SIZE-1) {
  76.         ptr = 0;
  77.         return(c & 0xff);
  78.     }
  79.                     /* hang on, wait and see */
  80.     if (maybe)
  81.         return(MAYBE);
  82.                     /* a clean miss */
  83.     ptr = 0;
  84.     return(c & 0xff);
  85. }
  86.  
  87. /*
  88.  * See if the two integer arrays "match".  Character parameters are
  89.  * designated by codes > 1000 and ASCII digit parameters are designated
  90.  * by codes > 2000.  Uses a simple linear search, so if NUM_VCS grows
  91.  * this routine will have to mature a bit.
  92.  */
  93.  
  94. static int
  95. match_codes(test, code, k)
  96. int test[], code[], k;
  97. {
  98.     extern int vcs_param[NUM_VCS][5];
  99.     register int i, j;
  100.     int pos, done;
  101.                     /* doesn't exist */
  102.     if (code[0] == -1)
  103.         return(NO);
  104.  
  105.     i = 0;
  106.     j = 0;
  107.     while (i<VCS_SIZE && j<VCS_SIZE) {
  108.                     /* at the end (a match) */
  109.         if (test[i] == -1 && code[j] == -1)
  110.             return(YES);
  111.                     /* ran out of input */
  112.         if (test[i] == -1)
  113.             break;
  114.         /*
  115.          * The char parameter (code 1000) always matches the
  116.          * next character.
  117.          */
  118.         if (code[j] >= 1000 && code[j] < 2000) {
  119.             pos = code[j] -1000;
  120.             vcs_param[k][pos] = test[i];
  121.             i++;
  122.             j++;
  123.             continue;
  124.         }
  125.         /*
  126.          * The digit parameter (code 2000) tries to match as many
  127.          * ASCII digits as it can.
  128.          */
  129.         if (code[j] >= 2000) {
  130.             pos = code[j] -2000;
  131.                     /* done with this number? */
  132.             if (vcs_param[k][pos])
  133.                 done = 1;
  134.             else
  135.                 done = 0;
  136.                     /* only digits */
  137.             while (test[i] >= 48 && test[i] <= 57) {
  138.                 if (!done)
  139.                     vcs_param[k][pos] = (vcs_param[k][pos] * 10) + test[i] -48;
  140.                 i++;
  141.             }
  142.                     /* ended in a digit */
  143.             if (test[i] == -1 && code[j+1] != -1) {
  144.                 vcs_param[k][pos] = 0;
  145.                 break;
  146.             }
  147.             j++;
  148.             continue;
  149.         }
  150.                     /* a clean miss */
  151.         if (test[i] != code[j]) {
  152.             for (j=0; j<5; j++)
  153.                 vcs_param[k][j] = 0;
  154.             return(NO);
  155.         }
  156.         i++;
  157.         j++;
  158.     }
  159.                     /* a maybe */
  160.     return(MAYBE);
  161. }
  162.  
  163. /*
  164.  * Build the table of VCS codes.  Actually we cheat... We tell curses(3)
  165.  * to build the strings to perform the function, and then we decipher
  166.  * what it did.
  167.  *
  168.  * For example: On a vt100 the cursor motion string in terminfo is:
  169.  *    cup=\E[%i%p1%d;%p2%dH$<5>
  170.  *
  171.  * This gets translated to the integer array vcs_code[] as:
  172.  *    \E   [   %p1%d  ;   %p2%d  H
  173.  *    27,  91, 2000,  59, 2001,  72
  174.  * 
  175.  * Notice that the "%p1" and "%p2" parameters get translated to 2000 and
  176.  * 2001.  This is to signify that the parameters are multiple digit ASCII
  177.  * encoded numbers.  The "%i" and "%d" codes are embedded into the vcs_opt[]
  178.  * array in somewhat of a loose manner.  In other words, there is no set
  179.  * format for the vcs_opt[] array.  The padding info "$<5>" is ignored.
  180.  */
  181.  
  182. void
  183. vcs_table()
  184. {
  185.     extern int vcs_codes[NUM_VCS][VCS_SIZE], vcs_opt[NUM_VCS][10];
  186.     extern int vcs_leadin[NUM_VCS], num_leadin, max_row, max_col;
  187.     int i, j, k, match, temp[VCS_SIZE];
  188.     static int substr();
  189.     char *p, *strcpy(), buf[VCS_SIZE], *getenv();
  190.     char *tparm();            /* comment out, if required */
  191.     static void fake_it();
  192.  
  193. #ifdef OLDCURSES
  194.     char tb[1024], *t, *cursor_home, *clr_eol, *clr_eos;
  195.     char *clear_screen, *cursor_up, *cursor_down, *cursor_right;
  196.     char *cursor_left, *cursor_address, *getenv(), *tgetstr(), *tgoto();
  197.  
  198.     tgetent(tb, getenv("TERM"));
  199.     t = tcbuf;
  200.  
  201.     cursor_home = tgetstr("ho", &t);
  202.     clr_eol = tgetstr("ce", &t);
  203.     clr_eos = tgetstr("cd", &t);
  204.     clear_screen = tgetstr("cl", &t);
  205.     cursor_up = tgetstr("up", &t);
  206.     cursor_down = tgetstr("do", &t);
  207.     cursor_right = tgetstr("nd", &t);
  208.     cursor_left = tgetstr("le", &t);
  209.     cursor_address = tgetstr("cm", &t);
  210.     max_row = tgetnum("li");
  211.     max_col = tgetnum("co");
  212. #else /* OLDCURSES */
  213.     setupterm(getenv("TERM"), 1, &i);
  214.     max_row = lines;
  215.     max_col = columns;
  216. #endif /* OLDCURSES */
  217.  
  218.     /*
  219.      * Do the easy ones first.  These don't take positional parameters,
  220.      * so all we have to do is strip the padding info.
  221.      */
  222.     for (i=0; i<NUM_VCS; i++) {
  223.         switch (i) {
  224.             case HOME:
  225.                 p = cursor_home;
  226.                 break;
  227.             case CLR_EOL:
  228.                 p = clr_eol;
  229.                 break;
  230.             case CLR_EOS:
  231.                 p = clr_eos;
  232.                 break;
  233.             case CLEAR:
  234.                 p = clear_screen;
  235.                 break;
  236.             case MV_UP:
  237.                 p = cursor_up;
  238.                 break;
  239.             case MV_DOWN:
  240.                 p = cursor_down;
  241.                 break;
  242.             case MV_RIGHT:
  243.                 p = cursor_right;
  244.                 break;
  245.             case MV_LEFT:
  246.                 p = cursor_left;
  247.                 break;
  248.             default:
  249.                 p = "";
  250.                 break;
  251.         }
  252.         /*
  253.          * Either the capability doesn't exist, or we're gonna
  254.          * do this one by hand (i.e.: ones with positional parameters)
  255.          */
  256.         if (!p) {
  257.             vcs_codes[i][0] = -1;
  258.             continue;
  259.         }
  260.                     /* fake an "output" */
  261.         fake_it(p);
  262.                     /* copy what it did */
  263.         j = 0;
  264.         while (putc_buf[j]) {
  265.             vcs_codes[i][j] = putc_buf[j];
  266.             j++;
  267.             if (j == VCS_SIZE-1)
  268.                 break;
  269.         }
  270.         vcs_codes[i][j] = -1;
  271.     }
  272.  
  273.     /*
  274.      * And now for the difficult ones.  The way it's done is: load the
  275.      * string with a few known parameters and then find where the
  276.      * parameters end up.  The vcs_opt[][] array is "free-flowing"
  277.      * and means something only to the routine being used.
  278.      */
  279.                     /* add one to the param */
  280.     if (substr(cursor_address, "%i") > 0)
  281.         vcs_opt[MV_DIRECT][0] = 1;
  282.                     /* decimal codes used */
  283.     if (substr(cursor_address, "%d") > 0)
  284.         vcs_opt[MV_DIRECT][1] = 1;
  285.                     /* character codes used */
  286.     if (substr(cursor_address, "%c") > 0)
  287.         vcs_opt[MV_DIRECT][2] = 1;
  288.                     /* add an offset */
  289.     if (substr(cursor_address, "%+") > 0)
  290.         vcs_opt[MV_DIRECT][3] = 1;
  291.                     /* subtract an offset */
  292.     if (substr(cursor_address, "%-") > 0)
  293.         vcs_opt[MV_DIRECT][4] = 1;
  294.                     /* load with parameters 12 & 34 */
  295. #ifdef OLDCURSES
  296.     fake_it(tgoto(cursor_address, 12, 34));
  297. #else /* OLDCURSES */
  298.     fake_it((char *)tparm(cursor_address, 12, 34));
  299. #endif /* OLDCURSES */
  300.  
  301.     j = 0;
  302.     while (putc_buf[j]) {
  303.         temp[j] = putc_buf[j];
  304.         j++;
  305.         if (j == VCS_SIZE-1)
  306.             break;
  307.     }
  308.     temp[j] = -1;
  309.                     /* if decimal parameters */
  310.     if (vcs_opt[MV_DIRECT][1]) {
  311.                     /* if add one */
  312.         if (vcs_opt[MV_DIRECT][0])
  313.             strcpy(buf, "13");
  314.         else
  315.             strcpy(buf, "12");
  316.                     /* where is the 12 (or 13)? */
  317.         if ((i = substr(putc_buf, buf)) > 0) {
  318.             temp[i] = 2000;
  319.             temp[i+1] = -2;
  320.         }
  321.         else
  322.             temp[0] = -1;
  323.                     /* if add one */
  324.         if (vcs_opt[MV_DIRECT][0])
  325.             strcpy(buf, "35");
  326.         else
  327.             strcpy(buf, "34");
  328.                     /* where is the 34 (or 35)? */
  329.         if ((i = substr(putc_buf, buf)) > 0) {
  330.             temp[i] = 2001;
  331.             temp[i+1] = -2;
  332.         }
  333.         else
  334.             temp[0] = -1;
  335.     }
  336.                     /* if character parameters */
  337.     if (vcs_opt[MV_DIRECT][2]) {
  338.                     /* original with 12 and 34 */
  339.         strcpy(buf, putc_buf);
  340.                     /* change 12 to 13 */
  341. #ifdef OLDCURSES
  342.         fake_it(tgoto(cursor_address, 13, 34));
  343. #else /* OLDCURSES */
  344.         fake_it((char *)tparm(cursor_address, 13, 34));
  345. #endif /* OLDCURSES */
  346.                     /* where are they different */
  347.         i = 0;
  348.         while (buf[i] != '\0') {
  349.             if (buf[i] != putc_buf[i])
  350.                 break;
  351.             i++;
  352.         }
  353.                     /* sanity checking */
  354.         if (buf[i] == '\0')
  355.             temp[0] = -1;
  356.                     /* if add, what is offset? */
  357.         if (vcs_opt[MV_DIRECT][3])
  358.             vcs_opt[MV_DIRECT][5] = temp[i] - 13;
  359.  
  360.                     /* if subtract, what is offset? */
  361.         if (vcs_opt[MV_DIRECT][4])
  362.             vcs_opt[MV_DIRECT][5] = 13 - temp[i];
  363.  
  364.         temp[i] = 1000;
  365.                     /* change 34 to 35 */
  366. #ifdef OLDCURSES
  367.         fake_it(tgoto(cursor_address, 12, 35));
  368. #else /* OLDCURSES */
  369.         fake_it((char *)tparm(cursor_address, 12, 35));
  370. #endif /* OLDCURSES */
  371.                     /* where are they different */
  372.         i = 0;
  373.         while (buf[i] != '\0') {
  374.             if (buf[i] != putc_buf[i])
  375.                 break;
  376.             i++;
  377.         }
  378.         temp[i] = 1001;
  379.         if (buf[i] == '\0')
  380.             temp[0] = -1;
  381.     }
  382.                     /* strip the -2's out, if any */
  383.     i = 0;
  384.     j = 0;
  385.     while (temp[i] != -1) {
  386.         if (temp[i] != -2)
  387.             vcs_codes[MV_DIRECT][j++] = temp[i];
  388.         i++;
  389.     }
  390.     vcs_codes[MV_DIRECT][j] = -1;
  391.  
  392.     /*
  393.      * Simplify the list.  Some codes are already handled by the
  394.      * virtual screen routines... no need to duplicate them.
  395.      */
  396.     if (vcs_codes[MV_DOWN][0] == '\n')
  397.         vcs_codes[MV_DOWN][0] = -1;
  398.  
  399.     if (vcs_codes[MV_LEFT][0] == 8)
  400.         vcs_codes[MV_LEFT][0] = -1;
  401.  
  402.     /*
  403.      * Often the "clear screen" sequence will contain the "home"
  404.      * sequence... if so, don't duplicate the "home" portion.
  405.      */
  406.     fake_it(cursor_home);
  407.     strcpy(buf, putc_buf);
  408.  
  409.     fake_it(clear_screen);
  410.                     /* if "home" inside "clear screen" */
  411.     if ((k = substr(putc_buf, buf)) >= 0) {
  412.                     /* if at the beginning */
  413.         if (k == 0) {
  414.             i = 0;
  415.             for (j=strlen(buf); j<VCS_SIZE; j++)
  416.                 vcs_codes[CLEAR][i++] = putc_buf[j];
  417.             vcs_codes[CLEAR][i] = -1;
  418.         }
  419.                     /* if at the end */
  420.         else if (strlen(buf)+k == strlen(putc_buf))
  421.             vcs_codes[CLEAR][k] = -1;
  422.     }
  423.                     /* is "clear screen" still unique */
  424.     k = 0;
  425.     for (i=0; i<NUM_VCS; i++) {
  426.         if (vcs_codes[CLEAR][i] == -1 || vcs_codes[CLR_EOS][i] == -1)
  427.             break;
  428.         if (vcs_codes[CLEAR][i] != vcs_codes[CLR_EOS][i]) {
  429.             k++;
  430.             break;
  431.         }
  432.     }
  433.     if (k == 0)
  434.         vcs_codes[CLEAR][0] = -1;
  435.  
  436.     /*
  437.      * Make a list of unique lead-in characters to be used as a
  438.      * simple hash table.
  439.      */
  440.     num_leadin = 0;
  441.     for (i=0; i<NUM_VCS; i++) {
  442.         if (vcs_codes[i][0] == -1)
  443.             continue;
  444.                     /* add any new lead-in character */
  445.         match = 0;
  446.         for (j=0; j<num_leadin; j++) {
  447.             if (vcs_leadin[j] == vcs_codes[i][0])
  448.                 match++;
  449.         }
  450.         if (!match)
  451.             vcs_leadin[num_leadin++] = vcs_codes[i][0];
  452.     }
  453.     return;
  454. }
  455.  
  456. /*
  457.  * The routine that fakes curses(3) into outputting the string info with
  458.  * the padding removed.
  459.  */
  460.  
  461. static void
  462. fake_it(s)
  463. char *s;
  464. {
  465.     static int fake_putc();
  466.  
  467.     putc_cnt = 0;
  468.     putc_buf[0] = '\0';
  469.     tputs(s, 1, fake_putc);
  470.     putc_buf[putc_cnt] = '\0';
  471.     return;
  472. }
  473. static int
  474. fake_putc(c)
  475. char c;
  476. {
  477.     if (c != '\0')
  478.         putc_buf[putc_cnt++] = c;
  479.     return(c);
  480. }
  481.  
  482. /*
  483.  * Is string2 contained in string1?  If so, return the offset, otherwise
  484.  * return a -1.
  485.  */
  486.  
  487. static int
  488. substr(s1, s2)
  489. char *s1, *s2;
  490. {
  491.     int i, len;
  492.  
  493.     len = strlen(s2);
  494.                     /* not possible */
  495.     if (len > strlen(s1))
  496.         return(-1);
  497.  
  498.     i = 0;
  499.     while (*s1) {
  500.         if (!strncmp(s1, s2, len))
  501.             return(i);
  502.         s1++;
  503.         i++;
  504.     }
  505.     return(-1);
  506. }
  507.